import re, time, json, logging, hashlib, base64, asyncio

import markdown2

from aiohttp import web

from coroweb import get, post
from apis import Page, APIValueError, APIResourceNotFoundError, APIError, APIPermissionError

from models import User, Comment, Blog, next_id
from config import configs

COOKIE_NAME = 'awesession'
_COOKIE_KEY = configs.session.secret


# 共21个API
# 后端API包括：
# 1.获取日志：GET /api/blogs
# 2.创建日志：POST /api/blogs
# 3.修改日志：POST /api/blogs/:blog_id
# 4.删除日志：POST /api/blogs/:blog_id/delete
# 5.获取评论：GET /api/comments
# 6.创建评论：POST /api/blogs/:blog_id/comments
# 7.删除评论：POST /api/comments/:comment_id/delete
# 8.创建新用户：POST /api/users
# 9.获取用户：GET /api/users
# 管理页面包括：
# 1.评论列表页：GET /manage/comments
# 2.日志列表页：GET /manage/blogs
# 3.创建日志页：GET /manage/blogs/create
# 4.修改日志页：GET /manage/blogs/
# 5.用户列表页：GET /manage/users
# 用户浏览页面包括：
# 1.注册页：GET /register
# 2.登录页：GET /signin
# 3.注销页：GET /signout
# 4.首页：GET /
# 5.日志详情页：GET /blog/:blog_id


# 加密cookie
def user2cookie(user, max_age):
    '''
    Generate cookie str by user.
    '''
    # build cookie string by: id-expires-sha1
    expires = str(int(time.time() + max_age))
    s = '%s-%s-%s-%s' % (user.id, user.passwd, expires, _COOKIE_KEY)
    L = [user.id, expires, hashlib.sha1(s.encode('utf-8')).hexdigest()]
    return '-'.join(L)


# 解密cookie
async def cookie2user(cookie_str):
    if not cookie_str:
        return None
    try:
        L = cookie_str.split('-')
        if len(L) != 3:
            return None
        uid, expires, sha1 = L
        if int(expires) < time.time():
            return None
        user = await User.find(uid)
        if user is None:
            return None
        s = '%s-%s-%s-%s' % (uid, user.passwd, expires, _COOKIE_KEY)
        if sha1 != hashlib.sha1(s.encode('utf-8')).hexdigest():
            logging.info("cookie校验失败")
            return None
        user.passwd = '******'
        return user
    except Exception as e:
        logging.exception(e)
        return None


# 转换文本格式
def text2html(text):
    lines = map(lambda s: '<p>%s</p>' % s.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;'),
                filter(lambda s: s.strip() != '', text.split('\n')))
    return ''.join(lines)


# 检查用户权限
def check_admin(request):
    if request.__user__ is None or not request.__user__.admin:
        raise APIPermissionError('用户无权限')


def get_page_index(page_str):
    p = 1
    try:
        p = int(page_str)
    except ValueError as e:
        pass
    if p < 1:
        p = 1
    return p


@get('/')
async def index(*, page='1'):
    page_index = get_page_index(page)
    num = await Blog.findNumber('count(id)')
    page = Page(num, page_index)
    if num == 0:
        blogs = []
    else:
        blogs = await Blog.findAll(orderBy='created_at desc', limit=(page.offset, page.limit))
    return {
        '__template__': 'blogs.html',
        'page': page,
        'blogs': blogs
    }


# 注册页面
@get('/register')
def register():
    return {
        '__template__': 'register.html'
    }


# 登陆页面
@get('/signin')
def signin():
    return {
        '__template__': 'signin.html'
    }


# 注销页面
@get('/signout')
def signout(request):
    referer = request.headers.get('Referer')
    r = web.HTTPFound(referer or '/')
    r.set_cookie(COOKIE_NAME, '-deleted-', max_age=0, httponly=True)
    logging.info('用户已退出')
    return r


_RE_EMAIL = re.compile(r'^[a-z0-9\.\-\_]+\@[a-z0-9\-\_]+(\.[a-z0-9\-\_]+){1,4}$')
_RE_SHA1 = re.compile(r'^[0-9a-f]{40}$')


# 登陆
@post('/api/authenticate')
async def authenticate(*, email, passwd):
    if not email:
        raise APIValueError('email', '请输入正确的Email地址')
    if not passwd:
        raise APIValueError('passwd', '请输入正确的密码')
    users = await User.findAll('email=?', [email])
    if len(users) == 0:
        raise APIValueError('email', '账户不存在')
    user = users[0]
    # 校验密码
    sha1 = hashlib.sha1()
    sha1.update(user.id.encode('utf-8'))
    sha1.update(b':')
    sha1.update(passwd.encode('utf-8'))
    if user.passwd != sha1.hexdigest():
        raise APIValueError('passwd', '密码错误！')

    # 校验成功，设置cookie
    r = web.Response()
    r.set_cookie(COOKIE_NAME, user2cookie(user, 86400), max_age=86400, httponly=True)
    user.passwd = '******'
    r.content_type = 'application/json'
    r.body = json.dumps(user, ensure_ascii=False).encode('utf-8')
    return r


# 注册
@post('/api/users')
async def api_register_user(*, email, name, passwd):
    if not name or not name.strip():
        raise APIValueError('请设置用户名')
    if not email or not _RE_EMAIL.match(email):
        raise APIValueError('邮箱格式不正确')
    if not passwd or not _RE_SHA1.match(passwd):
        raise APIValueError('passwd')
    users = await User.findAll('email=?', [email])  # 是否已注册
    if len(users) > 0:
        raise APIError('用户已注册')
    uid = next_id()
    sha1_passwd = '%s:%s' % (uid, passwd)
    user = User(id=uid, name=name.strip(), email=email, passwd=hashlib.sha1(sha1_passwd.encode('utf-8')).hexdigest(),
                image='http://www.gravatar.com/avatar/%s?d=mm&s=120' % hashlib.md5(email.encode('utf-8')).hexdigest())
    await user.save()

    r = web.Response()
    r.set_cookie(COOKIE_NAME, user2cookie(user, 86400), max_age=86400, httponly=True)
    user.passwd = '******'
    r.content_type = 'application/json'
    r.body = json.dumps(user, ensure_ascii=False).encode('utf-8')
    return r


# 创建评论
@post('/api/blogs/{id}/comments')
async def api_comments_create(request, *, id, content):
    blog = await Blog.find(id)
    if not blog:
        raise APIError('此博客不存在！')
    user_id = request.__user__.id
    comment = Comment(blog_id=id, user_id=request.__user__.id, user_name=request.__user__.name,
                      user_image=request.__user__.image, content=content.strip())
    await comment.save()
    return comment


# 分页获取评论
@get('/api/comments')
async def api(request, *, page='1'):
    check_admin(request)
    page_index = get_page_index(page)
    num = await Comment.findNumber('count(*)')
    p = Page(num, page_index)
    if num == 0:
        return dict(page=p, comments=())
    comments = await Comment.findAll(orderBy='created_at desc', limit=(p.offset, p.limit))
    return dict(page=p, comments=comments)


# 删除评论
@post('/api/comments/{id}/delete')
async def delete_comments(request, *, id):
    check_admin(request)
    comment = await Comment.find(id)
    if comment is None:
        raise APIError('此评论不存在！')
    await comment.remove()
    return dict(id=id)


# 创建Blog
@post('/api/blogs')
async def api_create_blog(request, *, name, summary, content):
    check_admin(request)
    if not name or not name.strip():
        raise APIValueError('name', '请输入博客名称')
    if not summary or not summary.strip():
        raise APIValueError('summary', '请输入博客简介')
    if not content or not content.strip():
        raise APIValueError('content', '请输入博客内容')
    blog = Blog(user_id=request.__user__.id, user_name=request.__user__.name, user_image=request.__user__.image,
                name=name.strip(), summary=summary.strip(), content=content.strip())
    await blog.save()
    return blog


# 编辑博客
@post('/api/blogs/{id}')
async def api_update_blog(id, request, *, name, summary, content):
    check_admin(request)
    blog = await Blog.find(id)
    if not name or not name.strip():
        raise APIValueError('name', 'name cannot be empty.')
    if not summary or not summary.strip():
        raise APIValueError('summary', 'summary cannot be empty.')
    if not content or not content.strip():
        raise APIValueError('content', 'content cannot be empty.')
    blog.name = name.strip()
    blog.summary = summary.strip()
    blog.content = content.strip()
    await blog.update()
    return blog


# 获取博客与相关评论
@get('/blog/{id}')
async def get_blog(id):
    blog = await Blog.find(id)
    if blog is None:
        raise APIError('此博客不存在')
    comments = await Comment.findAll('blog_id=?', [id], orderBy='created_at desc')
    for c in comments:
        c.html_content = text2html(c.content)
    blog.html_content = markdown2.markdown(blog.content)
    print(blog)
    return {
        '__template__': 'blog.html',
        'blog': blog,
        'comments': comments
    }


# 获取博客
@get('/api/blogs/{id}')
async def api_get_blog(*, id):
    blog = await Blog.find(id)
    return blog


# 分页获取Blog
@get('/api/blogs')
async def api_blogs(*, page='1'):
    page_index = get_page_index(page)
    num = await Blog.findNumber('count(id)')
    p = Page(num, page_index)
    if num == 0:
        return dict(page=p, blogs=())
    blogs = await Blog.findAll(orderBy='created_at desc', limit=(p.offset, p.limit))
    return dict(page=p, blogs=blogs)


# 删除博客
@post('/api/blogs/{id}/delete')
async def delete_blog(request, *, id):
    check_admin(request)
    blog = await Blog.find(id)
    if blog is None:
        raise APIError('此博客不存在！')
    await blog.remove()
    return dict(id=id)


# 分页获取用户
@get('/api/users')
async def api_get_users(*, page='1'):
    page_index = get_page_index(page)
    num = await User.findNumber('count(id)')
    p = Page(num, page_index)
    if num == 0:
        return dict(page=p, users=())
    users = await User.findAll(orderBy='created_at desc', limit=(p.offset, p.limit))
    for u in users:
        u.passwd = '******'
    return dict(page=p, users=users)


# 博客管理页面
@get('/manage/blogs')
def manage_blogs(*, page='1'):
    return {
        '__template__': 'manage_blogs.html',
        'page_index': get_page_index(page)
    }


# 创建博客页面
@get('/manage/blogs/create')
def manage_create_blog(request):
    check_admin(request)
    return {
        '__template__': 'manage_blog_edit.html',
        'id': '',
        'action': '/api/blogs'
    }


# 编辑博客页面
@get('/manage/blogs/edit')
async def manager_edit_blog(request, *, id):
    check_admin(request)
    blog = await Blog.find(id)
    if blog is None:
        raise APIError('此博客不存在！')
    return {
        '__template__': 'manage_blog_edit.html',
        'id': id,
        'action': '/api/blogs/%s' % id
    }


# 用户管理页面
@get('/manage/users')
async def manage_users(request, * , page=1):
    check_admin(request)
    return {
        '__template__': 'manage_users.html',
        'page_index': get_page_index(page)
    }


# 评论管理页面
@get('/manage/comments')
def manage_comments(*, page='1'):
    return {
        '__template__': 'manage_comments.html',
        'page_index': get_page_index(page)
    }
